﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Narwhunderful.Aphelia.Framework.Graphics;
using Narwhunderful.Aphelia.Framework.UI;

namespace Narwhunderful.Aphelia.Framework.UI
{
    public enum HorizontalTextAlignment
    {
        Left,
        Center,
        Right
    }

    public enum VerticalTextAlignment
    {
        Top,
        Center,
        Bottom
    }
}

namespace Narwhunderful.Aphelia.Framework.UI.Controls
{

    public class Label : UIComponent
    {

        int _spaceWidth;
        int _lineWidth;
        int _lineLength;

        List<TextLine> _clippedLines = new List<TextLine>();
        List<string> _words = new List<string>();
        int _currentClippedLine;
        int _totalClippedTextHeight;

        SpriteFont _font;
        public SpriteFont Font
        {
            get
            {
                return _font;
            }
            set
            {
                _font = value;
                if (_font != null) _spaceWidth = (int)_font.MeasureString(" ").X;
            }
        }

        string _desiredText = "";
        public string Text
        {
            get
            {
                return _desiredText;
            }
            set
            {
                _desiredText = value;
                base.NeedsUpdate = true;
            }
        }

        string[] _desiredLines;
        public string[] Lines
        {
            get
            {
                return _desiredLines;
            }
        }

        bool _isMultiline = false;
        public bool IsMultiline
        {
            get
            {
                return _isMultiline;
            }
            set
            {
                _isMultiline = value;
            }
        }

        bool _wordWrap = false;
        public bool WordWrap
        {
            get
            {
                return _wordWrap;
            }
            set
            {
                _wordWrap = value;
                base.NeedsUpdate = true;
            }
        }

        Color _foreColor = Color.White;
        public Color ForeColor
        {
            get
            {
                return _foreColor;
            }
            set
            {
                _foreColor = value;
            }
        }

        HorizontalTextAlignment _horizontalTextAlignment = HorizontalTextAlignment.Left;
        public HorizontalTextAlignment HorizontalTextAlignment
        {
            get
            {
                return _horizontalTextAlignment;
            }
            set
            {
                _horizontalTextAlignment = value;
                base.NeedsUpdate = true;
            }
        }

        VerticalTextAlignment _verticalTextAlignment = VerticalTextAlignment.Top;
        public VerticalTextAlignment VerticalTextAlignment
        {
            get
            {
                return _verticalTextAlignment;
            }
            set
            {
                _verticalTextAlignment = value;
                base.NeedsUpdate = true;
            }
        }

        public Label(HUD hud)
            : base(hud)
        {
            base.CanFocus = false;
        }

        protected override void InternalUpdate(GameTime gameTime)
        {
            base.InternalUpdate(gameTime);
            UpdateText();
        }

        protected virtual void UpdateText()
        {
            _lineWidth = 0;
            _lineLength = 0;

            if (_desiredText == null) _desiredText = "";
            if (_desiredText == "")
            {
                _desiredLines = null;
                return;
            }

            if (_isMultiline)
            {
                _clippedLines.Clear();
                _currentClippedLine = -1;
                _totalClippedTextHeight = 0;
                _desiredLines = _desiredText.Split(Environment.NewLine.ToCharArray());
                if (_desiredLines.Length <= 0) return;
            }
            else
            {
                _desiredLines = new string[] { _desiredText };
                if (_font != null) _lineWidth = (int)_font.MeasureString(_desiredText).X;
                _lineLength = _desiredText.Length;
            }

            for (int x = 0; x < _desiredLines.Length; x++)
            {
                ProcessLine(x);
            }

            for (int x = 0; x < _clippedLines.Count; x++)
            {
                AlignLine(x);
            }
        }

        bool CanAddNewLine()
        {
            return (_totalClippedTextHeight + _font.LineSpacing <= base.Bounds.Height);
        }

        protected virtual void ProcessLine(int line)
        {
            if (!CanAddNewLine()) return;

            _clippedLines.Add(new TextLine(_desiredLines[line]));
            _totalClippedTextHeight += _font.LineSpacing;

            _lineWidth = (int)_font.MeasureString(_clippedLines[_currentClippedLine].Text).X;
            _lineLength = _clippedLines[_currentClippedLine].Text.Length;

            _currentClippedLine++;

            _words.Clear();
            if (_isMultiline)
            {
                if (_wordWrap) _words.AddRange(_clippedLines[_currentClippedLine].Text.Split(new char[] { Convert.ToChar(" ") }));

                while (_lineWidth > base.Bounds.Width)
                {
                    if (_wordWrap) 
                    {
                        if (_words.Count > 0)
                        {
                            // Check if the current word exceeds the width of this control.
                            if (_font.MeasureString(_words[_words.Count - 1]).X <= base.Bounds.Width)
                            {
                                if (_currentClippedLine + 1 >= _clippedLines.Count)
                                {
                                    if (!CanAddNewLine()) return;
                                    _clippedLines.Add(new TextLine(_words[_words.Count - 1]));
                                }
                                else
                                {
                                    _clippedLines[_currentClippedLine + 1].Text = _words[_words.Count - 1] + " " + _clippedLines[_currentClippedLine + 1].Text;
                                }
                            }
                            _lineLength -= _words[_words.Count - 1].Length + 1;
                            _words.RemoveAt(_words.Count - 1);
                        }
                    }

                    if (_lineLength < 0) _lineLength = 0;

                    _clippedLines[_currentClippedLine].Text = _clippedLines[_currentClippedLine].Text.Substring(0, _lineLength);
                    _lineWidth = (int)_font.MeasureString(_clippedLines[_currentClippedLine].Text).X;

                    if (_wordWrap) // Repeat the loop for the recently added clipped line if necessary.
                    {
                        if (_lineWidth < base.Bounds.Width)
                        {
                            if (_currentClippedLine + 1 < _clippedLines.Count)
                            {
                                _currentClippedLine++;
                                _words.Clear();
                                _words.AddRange(_clippedLines[_currentClippedLine].Text.Split(new char[] { Convert.ToChar(" ") }, StringSplitOptions.RemoveEmptyEntries));
                                _lineLength = _clippedLines[_currentClippedLine].Text.Length;
                                _lineWidth = (int)_font.MeasureString(_clippedLines[_currentClippedLine].Text).X;
                            }
                        }
                    }
                }
            }
        }

        protected virtual void AlignLine(int clippedLine)
        {
            _lineWidth = (int)_font.MeasureString(_clippedLines[clippedLine].Text).X;

            switch (_horizontalTextAlignment)
            {
                case HorizontalTextAlignment.Left:
                    _clippedLines[clippedLine].Position.X = base.Bounds.X;
                    break;
                case HorizontalTextAlignment.Center:
                    _clippedLines[clippedLine].Position.X = base.Bounds.X + (base.Bounds.Width / 2) - (_lineWidth / 2);
                    break;
                case HorizontalTextAlignment.Right:
                    _clippedLines[clippedLine].Position.X = base.Bounds.Right - _lineWidth;
                    break;
            }

            switch (VerticalTextAlignment)
            {
                case VerticalTextAlignment.Top:
                    _clippedLines[clippedLine].Position.Y = base.Bounds.Y;
                    break;
                case VerticalTextAlignment.Center:
                    _clippedLines[clippedLine].Position.Y = base.Bounds.Y + (base.Bounds.Height / 2) - (_clippedLines.Count * _font.LineSpacing / 2);
                    break;
                case VerticalTextAlignment.Bottom:
                    _clippedLines[clippedLine].Position.Y = base.Bounds.Bottom - _clippedLines.Count * _font.LineSpacing;
                    break;
            }
            _clippedLines[clippedLine].Position.Y += clippedLine * _font.LineSpacing;
        }

        protected override void InternalDrawMiddleground(GameTime gameTime)
        {
            base.InternalDrawMiddleground(gameTime);
            if (_desiredText == "") return;
            for (int x = 0; x < _clippedLines.Count; x++)
            {
                base.SpriteBatch.DrawString(_font, _clippedLines[x].Text, _clippedLines[x].Position, _foreColor);
            }
        }

    }

    internal class TextLine
    {

        public string Text = "";
        public Vector2 Position = Vector2.Zero;

        public TextLine(string text)
        {
            this.Text = text;
        }

        public TextLine(string text, Vector2 position)
            : this(text)
        {
            this.Text = text;
            this.Position = position;
        }

    }

}
